﻿
var PMChat = jQuery.extend(PMChat || {},
{
    NullChannel: function (config) {
        this.Config = {};

        jQuery.extend(this.Config, config);

        this.Init = function () { };

        this.Open = function () {
        };

        this.Close = function () {
        };
    },

    PollerChannel: function (config) {

        this._trackMembers = {};

        this.Config = {
            PollInterval: 5000
        };

        jQuery.extend(this.Config, config);

        this.On = function (event, handler) {
            jQuery(this).on(event, handler);
            return this;
        };

        this.Off = function (event, handler) {
            jQuery(this).off(event, handler);
            return this;
        };

        this.Trigger = function (event, param) {
            jQuery(this).trigger(event, param);
            return this;
        };

        this.AddTrackMember = function (member) {
            if (!this._trackMembers[member.MemberID]) {
                this._trackMembers[member.MemberID] = member;
            }
        };

        this.RemoveTrackMember = function (member) {
            if (!this._trackMembers[member.MemberID])
                return;

            delete this._trackMembers[member.MemberID];
        };

        this.StopTrackingMembers = function () {
        };

        this.StartTrackingMembers = function () {
        };

        this.Open = function () {
            this.On("poll", this._ChatPoller);
            this.On("poll", this._PresencePoller);
        };

        this.Close = function () {
            this.Off("poll", this._PresencePoller);
            this.Off("poll", this._ChatPoller);
        };

        this.Init = function () {
            var self = this;

            if (self.Config.PollInterval && self.Config.PollInterval > 0) {
                window.setInterval(function () { self.Trigger("poll"); }, self.Config.PollInterval);
            }
        };

        this._ChatPoller = function () {
            PMChat.GetNewChats();
        };

        this._PresencePoller = function () {
            var request = PMChat.Config.API.BaseUrl + "/presence/get?";
            var mList = "";

            for (var m in this._trackMembers) {
                if (m == "undefined" || Object.prototype.toString.call(clients[m]) == "[object Function]") continue;

                if (this._trackMembers[m] != null) {
                    if (mList.length > 0) mList += "&";
                    mList += "m=" + this._trackMembers[m].MemberID;
                }
            }

            if (mList.length == 0) return;

            request += mList;

            PMChat.ajax({
                type: "GET",
                url: request,
                success: function (results) {
                    if (typeof results === 'string') {
                        results = JSON.parse(results);
                    };

                    for (var i in results) {
                        if (Object.prototype.toString.call(results[i]) == "[object Function]") continue;
                        var result = results[i];

                        if (this._trackMembers[result.MemberID]) {
                            if (!this._trackMembers[result.MemberID].Status) this._trackMembers[result.MemberID].Status = { Presence: 0 };
                            if (this._trackMembers[result.MemberID].Status.Presence != result.Status.Presence) {
                                result.OriginalStatus = this._trackMembers[result.MemberID].Status;
                                this._trackMembers[result.MemberID].Status = result.Status;
                                PMChat._ProcessChatEvent({ Type: "Presence", Value: result });
                            }
                        }
                    }
                },
                error: new PMChat.ErrorHandler(function (err) {
                    var e = jQuery.Event("getpresenceserror", { Error: err });
                    PMChat.Trigger(e);
                })
            });
        };

    },

    WebSyncChannel: function (config) {

        this.Client = null;
        this._trackMembers = { };

        this.Config = {
            Client: null,
            Channel: "/chat/{SiteID}/{MemberID}",
            FallBackChannel: new PMChat.PollerChannel()
        };
        
        jQuery.extend(this.Config, config);

        this.On = function (event, handler) {
            jQuery(this).on(event, handler);
            return this;
        };

        this.Off = function (event, handler) {
            jQuery(this).off(event, handler);
            return this;
        };

        this.Trigger = function (event, param) {
            jQuery(this).trigger(event, param);
            return this;
        };

        this._trackSettings = function (member) {
            var self = this;
            return {
                channel: "/presence/" + member.SiteID + "/" + member.MemberID,
                onSuccess: function(args) {
                    if (args.isResubscribe) {
                        PMChat.Log('Resubscribed to Presence!');
                    } else {
                        PMChat.Log('Subscribed to Presence!');
                    }
                },
                onFailure: function(args) {
                    if (args.isResubscribe) {
                        PMChat.Log('Could not resubscribe to presence: ' + args.error);
                    } else {
                        PMChat.Log('Could not subscribe to presence: ' + args.error);
                    }
                },
                onReceive: function (args) {
                    var data = null;
                    if (args.getData) {
                        data = args.getData();
                    } else if (args.data) {
                        data = args.data;
                    }

                    var request = PMChat.Config.API.BaseUrl + "/presence/get?m=" + data.MemberID;

                    PMChat.ajax({
                        type: "GET",
                        url: request,
                        success: function(results) {
                            if (typeof results === 'string') {
                                results = JSON.parse(results);
                            }

                            for (var i in results) {
                                if (Object.prototype.toString.call(results[i]) == "[object Function]") continue;
                                var result = results[i].Member;

                                if (self._trackMembers[result.MemberID]) {
                                    if (!self._trackMembers[result.MemberID].Status) {
                                        self._trackMembers[result.MemberID].Status = { Presence: 0 };
                                    }

                                    result.OriginalStatus = self._trackMembers[result.MemberID].Status;
                                    self._trackMembers[result.MemberID].Status = result.Status;
                                    PMChat._ProcessChatEvent({ Type: "Presence", Value: result });
                                }
                            }
                        },
                        error: new PMChat.ErrorHandler(function (err) {
                            var e = jQuery.Event("getpresenceerror", { Error: err });
                            PMChat.Trigger(e);
                        })
                    });
                }
        };
        };

        this.AddTrackMember = function(member) {
            if (!this._trackMembers[member.MemberID]) {
                this._trackMembers[member.MemberID] = member;
                var self = this;
                this.Client.subscribe(this._trackSettings(member));
            }
        };

        this.RemoveTrackMember = function (member) {
            if (!this._trackMembers[member.MemberID])
                return;

            delete this._trackMembers[member.MemberID];

            var chan = "/presence/" + member.SiteID + "/" + member.MemberID;

            this.Client.unsubscribe({ channel: chan });
        };

        this.StopTrackingMembers = function () {
            for (var m in this._trackMembers) {
                var member = this._trackMembers[m];

                var chan = "/presence/" + member.SiteID + "/" + member.MemberID;
                this.Client.unsubscribe({ channel: chan });
            }
        };

        this.StartTrackingMembers = function () {
            for (var m in this._trackMembers) {
                var member = this._trackMembers[m];
                this.Client.subscribe(this._trackSettings(member));
            }
        };

        this.Open = function() {
            var self = this;

            try {
                if (self.Client.getIsConnected() == false && self.Client.getIsConnecting() == false) {
                    self.Client.connect();
                } else {
                    self._Subscribe();
                }
            } catch (err) { }
        };

        this.Close = function () {
            var args = new fm.websync.unsubscribeArgs();
            args.setChannel(this.Config.Channel);

            this.Client.unsubscribe(args);

            this.StopTrackingMembers();

            if (this.Config.FallBackChannel != null)
                this.Config.FallBackChannel.Close();
        };

        this.Init = function () {
            var self = this;

            if (self.Config.Client != null) {
                try {
                    if (typeof self.Config.Client == "function") {
                        self.Client = self.Config.Client();
                    } else {
                        self.Client = self.Config.Client;
                    }
                } catch (err) { PMChat.Log(err.message); }
            }

            self.Client.addOnConnectSuccess(function (args) {
                if (args.getIsReconnect()) {
                    PMChat.Log('Reconnected!');
                } else {
                    PMChat.Log('Connected!');
                }
                if (self.Config.FallBackChannel != null)
                    self.Config.FallBackChannel.Close();
            });

            self.Client.addOnConnectFailure(function (args) {
                if (args.getIsReconnect()) {
                    PMChat.Log('Could not reconnect: ' + args.getErrorMessage());
                } else {
                    PMChat.Log('Could not connect: ' + args.getErrorMessage());
                }
                if (self.Config.FallBackChannel != null)
                    self.Config.FallBackChannel.Open();
            });

            self.Client.addOnStreamFailure(function (args) {
                if (args.getWillReconnect && args.getWillReconnect()) {
                    PMChat.Log('Connection to server lost, reconnecting...');
                } else {
                    PMChat.Log('Connection to server lost permanently.');
                }
                if (self.Config.FallBackChannel != null)
                    self.Config.FallBackChannel.Open();
            });

            if (self.Config.FallBackChannel != null)
                self.Config.FallBackChannel.Init();

        };

        this._Subscribe = function () {
            var self = this;
            this.Client.subscribe({
                channel: this.Config.Channel,
                onSuccess: function (args) {
                    if (args.isResubscribe) {
                        PMChat.Log('Resubscribed!');
                    } else {
                        PMChat.Log('Subscribed!');
                    }
                    self.StartTrackingMembers();
                },
                onFailure: function (args) {
                    if (args.isResubscribe) {
                        PMChat.Log('Could not resubscribe: ' + args.error);
                    } else {
                        PMChat.Log('Could not subscribe: ' + args.error);
                    }
                },
                onReceive: function (args) {
                    var data = null;
                    if (args.getData) {
                        data = args.getData();
                    } else if (args.data) {
                        data = args.data;
                }

                    PMChat._ProcessChatEvent(data);
                }
            });
        };
    },

    NullState: function (config) {
        this.Config = {};

        jQuery.extend(this.Config, config);
        
        this.Init = function () { };

        this.LoadState = function () {
            return {};
        };

        this.SaveState = function (state) {
        };
    },

    WebStorageState: function (config) {
        this.Config = {};

        jQuery.extend(this.Config, config);

        this.Init = function () { };

        this.LoadState = function () {
            var state = {};
            if (this.SupportsWebStorage()) {
                state = window.sessionStorage.getItem("PMChat.State." + PMChat.Config.NickName);
            } else if (jQuery.cookie) {
                state = jQuery.cookie("PMChat.State." + PMChat.Config.NickName);
            }

            try {
                state = JSON.parse(state);
            } catch (err) { state = null; }

            return state || {};
        };

        this.SaveState = function (state) {
            if (this.SupportsWebStorage()) {
                window.sessionStorage.setItem("PMChat.State." + PMChat.Config.NickName, JSON.stringify(state));
            } else if (jQuery.cookie) {
                jQuery.cookie("PMChat.State." + PMChat.Config.NickName, JSON.stringify(state))
            }
        };

        this.SupportsWebStorage = function() {
            try {
                if ('localStorage' in window && window['localStorage'] !== null)
                {
                    window.sessionStorage.setItem('PMChat.State.Test', '1');
                    window.sessionStorage.removeItem('PMChat.State.Test');
                }
            } catch(e) {
                return false;
            }
        };
    }
});

PMChat = jQuery.extend(PMChat || {},
{
    Connected: false,

    Config: {
        SiteID: 0,
        MemberID: "NONE",
        NickName: "UNKNOWN",
        SessionToken: "",
        State: new PMChat.NullState(),
        Channel: new PMChat.NullChannel(),
        BaseUrl: "/chat/v1",
        API: {
            BaseUrl: "/chat/v1/api"
        }
    },

    _Clients: {},

    Log: function (msg) {
        try { window.console.log("Chat: ", msg); } catch (e) { }
        return PMChat;
    },

    Init: function (config) {

        jQuery.extend(this.Config, config);
        
        if (!window.console) window.console = {};
        if (!window.console.log) window.console.log = function () { };
        if (!window.console.error) window.console.error = function () { };

        if (Object.prototype.toString.call(this.Config.SessionToken) == "[object Function]") {
            this.Config.SessionToken = this.Config.SessionToken();
        }

        this.Config.Channel.Init();
        this.Config.State.Init();

        var e = new jQuery.Event("ready");
        this.Trigger(e);

        var state = PMChat.Config.State.LoadState();

        if (state == null || state.Connected != false) {
            PMChat.Connect();
        }

        return this;
    },

    Connect: function () {
        if (this.Connected == false) {
            this.Config.Channel.Open();
            this.Connected = true;

            this.LoadState();
            this.SaveState();

            var e = new jQuery.Event("connected");
            this.Trigger(e);
        }
    },

    Disconnect: function () {
        if (this.Connected == true) {
            this.SaveState();

            this.Config.Channel.Close();
            this.Connected = false;

            this.SaveState();

            for (var m in this._Clients) {
                if (m == "undefined" || Object.prototype.toString.call(this._Clients[m]) == "[object Function]") continue;

                if (this._Clients[m] != null) {
                    this._Clients[m].Close();
                }
            }

            var e = new jQuery.Event("disconnected");
            this.Trigger(e);
        }
    },

    LoadState: function () {
        var e = new jQuery.Event("beforestateloaded");
        this.Trigger(e);

        var state = this.Config.State.LoadState();

        if (state.Clients) {
            for (var m in state.Clients) {
                if (m == "undefined" || Object.prototype.toString.call(state.Clients[m]) == "[object Function]") continue;

                var withMember = state.Clients[m];
                if (withMember == null) {
                    continue;
                }

                withMember.LoadedFromState = true;
                PMChat.Client(withMember, { SaveState: false });
            }
        }

        e = new jQuery.Event("stateloaded");
        this.Trigger(e, state);
    },

    SaveState: function () {
        var clients = [];
        
        for (var m in this._Clients) {
            if (m == "undefined" || Object.prototype.toString.call(this._Clients[m]) == "[object Function]") continue;
            
            if (this._Clients[m] != null && this._Clients[m].WithMember != null) {
                clients.push(this._Clients[m].WithMember);
            }
        }

        var state = { Connected: PMChat.Connected, Clients: clients };
        
        var e = new jQuery.Event("beforestatesaved");
        this.Trigger(e, state);

        this.Config.State.SaveState(state);

        e = new jQuery.Event("statesaved");
        this.Trigger(e);
    },

    ajax: function (settings) {
        var useJsonp = (jQuery('html').hasClass('lt-ie8')) ? true : false;

        if (useJsonp == true) {
            // this means we are in IE7 or lower :(
        settings = jQuery.extend(settings, {
            global: false,
                type: "GET", // force it to be a GET
                originalType: settings.type,
                dataType: "jsonp", // jsonp request
                data: null,
                originalData: settings.data,
                beforeSend: function (xhr, settings) {
                    settings.url += (settings.url.indexOf("?") > -1 ? "&" : "?") + "a=" + PMChat.Config.SessionToken;

                    settings.url += "&jsonp=true";
                    // pass the original HTTP method as query string so Web API knows how to process it
                    settings.url += "&httpmethod=" + settings.originalType;
                    if (settings.originalData && settings.originalData != null) {
                        // pass the json "data" in the query string
                        settings.url += "&jsonpbody=" + JSON.stringify(settings.originalData);
                        settings.data = null;
                    }
                }
            });
        } else {
            var chatHeaders = {};
            if (PMChat.Config.SessionToken != null && PMChat.Config.SessionToken.length > 0) {
                chatHeaders.Authorization = "Basic " + PMChat.Config.SessionToken;
            }
            
            settings = jQuery.extend(settings, {
                global: false,
            dataType: "json",
            headers: jQuery.extend(settings.headers, chatHeaders),
            beforeSend: function (xhr, settings) {
                if (window.XDomainRequest) {
                    settings.url += (settings.url.indexOf("?") > -1 ? "&" : "?") + "a=" + PMChat.Config.SessionToken;
                }
            }
        });
        }

        if (window.XDomainRequest && jQuery.ajaxQueue) {
            return jQuery.ajaxQueue(settings);
        }
        else {
            return jQuery.ajax(settings);
        }
    },

    GetNewChats: function () {
        if (this.Connected == false)
            return;

        var request = PMChat.Config.API.BaseUrl + "/chat/get";
        PMChat.ajax({
            type: "GET",
            url: request,
            success: function (results) {
                if (typeof results === 'string') {
                    results = JSON.parse(results);
                };

                var newMsgMembers = [];

                for (var i in results) {
                    if (Object.prototype.toString.call(results[i]) == "[object Function]") continue;

                    var hasNewMsg = false;
                    var msgList = JSON.parse(JSON.stringify(results[i].Messages)).reverse();
                    for (var m in msgList) {
                        var chatMsg = msgList[m];
                        if (Object.prototype.toString.call(chatMsg) == "[object Function]") continue;
                        if (chatMsg.Acknowledged == false && chatMsg.Sender.MemberID != PMChat.Config.MemberID) {
                            hasNewMsg = true;
                        }

                        PMChat._ProcessChatEvent({ Type: "ChatMessage", Value: chatMsg }, { SupressNewMsgEvent: true });
                    }

                    if (hasNewMsg == true) {
                        newMsgMembers.push(results[i].WithMember);
                    }
                    }

                if (newMsgMembers.length > 0) {
                    var member = newMsgMembers[0];
                    var client = PMChat.Client(member);

                    var e = jQuery.Event("hasnewmessage", { Client: client });
                    client.Trigger(e);
                }

            },
            error: new PMChat.ErrorHandler(function (err) {
                var e = jQuery.Event("getchatserror", { Error: err });
                PMChat.Trigger(e);
            })
        });
    },

    GetContacts: function () {
        if (this.Connected == false)
            return;

        var request = PMChat.Config.API.BaseUrl + "/contacts/get";
        PMChat.ajax({
            type: "GET",
            url: request,
            success: function (results) {

                if (typeof results === 'string') {
                    results = JSON.parse(results);
                };

                var contacts = [ ];

                for (var i in results) {
                    if (Object.prototype.toString.call(results[i]) == "[object Function]") continue;

                    contacts.push(results[i]);
                }

                var e = jQuery.Event("getcontacts");
                PMChat.Trigger(e, [ contacts ]);
            },
            error: new PMChat.ErrorHandler(function (err) {
                var e = jQuery.Event("getcontactserror", { Error: err });
                PMChat.Trigger(e);
            })
        });
    },

    AddTrackMember: function(member) {
        this.Config.Channel.AddTrackMember(member);
    },

    RemoveTrackMember: function (member) {
        this.Config.Channel.RemoveTrackMember(member);
    },

    StopTrackingMembers: function () {
        this.Config.Channel.StopTrackingMembers();
    },

    StartTrackingMembers: function () {
        this.Config.Channel.StartTrackingMembers();
    },

    On: function (event, handler) {
        jQuery(this).on(event, handler);
        return this;
    },

    Off: function (event, handler) {
        jQuery(this).off(event, handler);
        return this;
    },

    Trigger: function (event, param) {
        jQuery(this).trigger(event, param);
        return this;
    },

    ClientByName: function (withName) {
        for (var client in this._Clients) {
            if (client.WithMember.NickName.toLowerCase() == withName.toLowerCase())
                return client;
        }

        return null;
    },

    Client: function (withMember, options) {
        if (typeof withMember === 'string') {
            return this.ClientByName(withMember);
        }

        var options = jQuery.extend({ SaveState: true, ClientMustExist: false }, options);

        var clientID = withMember.MemberID;

        // make sure we aren't chatting with ourselves 
        if (clientID == PMChat.Config.MemberID) return null;

        var isNewClient = false;
        if (!this._Clients[clientID]) {
            isNewClient = true;
            if (options.ClientMustExist != true) {
                this._Clients[clientID] = new PMChat._PMChatClient(withMember);
            } else {
                return null;
            }
        } else {
            isNewClient = false;
            this._Clients[clientID].WithMember = jQuery.extend(this._Clients[clientID].WithMember, withMember);
        }

        if (isNewClient == true) {
            this.Config.Channel.AddTrackMember(withMember);

            var e = jQuery.Event("open", { Client: this.Client(withMember) });
            this.Trigger(e);
        }

        if (this.Connected == true && options.SaveState == true) {
            this.SaveState();
        }

        return this._Clients[clientID];
    },

    Clients: function () {
        return this._Clients;
    },

    Close: function (client) {
        var clientProp = this._Clients[client.WithMember.MemberID];
        if (clientProp && clientProp != null) {
            delete this._Clients[clientProp.WithMember.MemberID];
            var e = jQuery.Event("close", { Client: clientProp });
            this.Trigger(e);
        }

        if (this.Connected == true) {
            PMChat.SaveState();
        }
    },

    DataHandlers: {
        PingRequest: function (data, options) {
        },
        Presence: function (data, options) {
            var e = jQuery.Event("presence", { Client: PMChat.Client(data, { SaveState: false, ClientMustExist: true }), Member: data });
            if (e.Client != null) {
                e.Client.Trigger(e, { Status: data.Status, OriginalStatus: data.OriginalStatus });
            }

            PMChat.Trigger(e, { Status: data.Status, OriginalStatus: data.OriginalStatus });
        },
        ChatComposing: function (data, options) {
            var withMember = data.Sender;
            if (withMember.MemberID == PMChat.Config.MemberID) {
                withMember = data.Recipient;
            }

            var client = PMChat.Client(withMember, { ClientMustExist: true });

            if (client && client != null) {
                var e = jQuery.Event("composing", { Client: client });
                e.Client.Trigger(e, data);
            }
        },
        AckMessage: function (data, options) {
            var withMember = data.Sender;
            if (withMember.MemberID == PMChat.Config.MemberID) {
                withMember = data.Recipient;
            }

            var e = jQuery.Event("ack", { Client: PMChat.Client(withMember) });
            e.Client.Trigger(e, data);
        },
        ChatMessage: function (data, options) {
        options = jQuery.extend({
            SupressNewMsgEvent: false
        }, options);

            var withMember = data.Sender;
            if (withMember.MemberID == PMChat.Config.MemberID) {
                withMember = data.Recipient;
        }

            if (withMember == data.Sender) {
                // Send ACK
                var request = PMChat.Config.API.BaseUrl + "/chat/ack";
                PMChat.ajax({
                    type: "POST",
                    url: request,
                    data: {
                        MessageID: data.MessageID
                    },
                    success: function (results) {
                    },
                    error: new PMChat.ErrorHandler(function (err) {
                        var e = jQuery.Event("ackerror", { Client: PMChat.Client(withMember), Error: err });
                        e.Client.Trigger(e, { WithMemberID: withMember.MemberID, Message: data });
                    })
                });
            }

            var e = jQuery.Event("message", { Client: PMChat.Client(withMember) });
            e.Client.Trigger(e, data);

            if (options.SupressNewMsgEvent != true && data.Acknowledged != true) {
                if (data.Sender.MemberID != PMChat.Config.MemberID) {
                e = jQuery.Event("hasnewmessage", { Client: PMChat.Client(withMember) });
                    e.Client.Trigger(e);
                }
            }
        }
    },

    _ProcessChatEvent: function (data, options) {
        if (PMChat.Connected == false)
        return;

        var handler = null;
        if (PMChat.DataHandlers[data.Type]) {
            handler = PMChat.DataHandlers[data.Type];
        }

        if (handler != null) {
            handler(data.Value, options);
        } else {
            PMChat.Log("Unkown data package type: " + data.Type);
        }
    },

    _PMChatClient: function (withMember) {

        this.WithMember = withMember;

        this.Log = function (msg) {
            PMChat.Log(this.WithMember.NickName + ": " + msg);
        };

        this.Send = function (message) {
            if (PMChat.Connected == false)
                return;

            var client = this;
            var request = PMChat.Config.API.BaseUrl + "/chat/send";
            var withMemberId = this.WithMember.MemberID;
            PMChat.ajax({
                type: "POST",
                url: request,
                data: {
                    WithMemberID: withMemberId,
                    Message: message
                },
                success: function (response) {
                    PMChat._ProcessChatEvent({ Type: "ChatMessage", Value: response });

                    var e = jQuery.Event("sent", { Client: client });
                    e.Client.Trigger(e, { WithMemberID: withMemberId, Message: message });
                },
                error: new PMChat.ErrorHandler(function (err) {
                    var e = jQuery.Event("senderror", { Client: client, Error: err });
                    e.Client.Trigger(e, { WithMemberID: withMemberId, Message: message });
                })
            });
        };

        this.Get = function (settings) {
            if (PMChat.Connected == false)
                return;

            settings = jQuery.extend({ LastMessageID: "", MessageCount: 20 }, settings);
            var client = this;
            var request = PMChat.Config.API.BaseUrl + "/chat/get";
            var withMemberId = this.WithMember.MemberID;
            var parameters = "m=" + withMemberId;

            if (settings.LastMessageID != null && settings.LastMessageID.length != "")
                parameters += ("&s=" + settings.LastMessageID);

            if (settings.MessageCount != null && settings.MessageCount > 0)
                parameters += ("&c=" + settings.MessageCount);

            PMChat.ajax({
                type: "GET",
                url: request + "?" + parameters,
                success: function (response) {
                    client.WithMember = jQuery.extend(response.WithMember, this.WithMember);
                    var e = jQuery.Event("getchat", { Client: client });
                    e.Client.Trigger(e, { WithMemberID: withMemberId, Chat: response });
                },
                error: new PMChat.ErrorHandler(function (err) {
                    var e = jQuery.Event("getchaterror", { Client: client, Error: err });
                    e.Client.Trigger(e, { WithMemberID: withMemberId });
                })
            });
        },

        this.Composing = function (isComposing) {
            if (PMChat.Connected == false)
                return;

            var client = this;
            var request = PMChat.Config.API.BaseUrl + "/chat/composing";
            var withMemberId = this.WithMember.MemberID;
            PMChat.ajax({
                type: "POST",
                url: request,
                data: {
                    WithMemberID: withMemberId,
                    IsComposing: isComposing
                },
                success: function (response) {
                },
                error: new PMChat.ErrorHandler(function (err) {
                    var e = jQuery.Event("composingerror", { Client: client, Error: err });
                    e.Client.Trigger(e, { WithMemberID: withMemberId });
                })
            });
        };

        this.Close = function () {
            PMChat.Close(this);
        };

        this.On = function (event, handler) {
            jQuery(this).on(event, handler);
            return this;
        };

        this.Off = function (event, handler) {
            jQuery(this).off(event, handler);
            return this;
        };

        this.Trigger = function (event, param) {
            jQuery(this).trigger(event, param);
            return this;
        };

        return this;
    },

    ErrorManager: {
        LastError: null,
        ServerErrors: {
            general: {
                "0": "An unknown error has occurred",
                "4401": "Your chat session has expired.  Please refresh the page to continue."
            },
            validation: {
                "401": "You must be a paid member to participate in chat",
                "501": "The recipient member could not be found",
                "502": "The member is in your blocked list"
            }
        },

        GetError: function (errorCode, type) {
            if (type == null) {
                type = "Server";
            }

            for (var field in this[type + "Errors"]) {
                if (this[type + "Errors"][field][errorCode]) {
                    var result = {};
                    result[field] = this[type + "Errors"][field][errorCode];
                    return result;
                }
            }
            return null;
        },

        GetErrorMessage: function (errorObj, type) {
            var result = errorObj.getMessage();

            if (type == null) {
                type = "Server";
            }

            var errorCode = errorObj.getCode();

            for (var field in this[type + "Errors"]) {
                if (this[type + "Errors"][field][errorCode]) {
                    result = this[type + "Errors"][field][errorCode];
                    break;
                }
            }

            return result;
        }
    },

    ErrorHandler: function (onError) {
        var self = this;

        this.Handler = onError;

        this.OnError = function (jqXHR, textStatus, errorThrown) {
            try {
            var err = self.getErrorObject(jqXHR, textStatus, errorThrown);
            PMChat.ErrorManager.LastError = err;

                err.Handled = false;

            if (self.Handler) {
                self.Handler(err);
            }

                var e = jQuery.Event("error", { Error: err });
                PMChat.Trigger(e);
            } catch (ex) { }
        };

        this.getErrorObject = function (jqXHR, textStatus, errorThrown) {
            var error = { Message: "Unknown Error" };
            if (!errorThrown) {
                if (textStatus && textStatus != "error") {
                    error.Message = textStatus;
                } else if (jqXHR.statusText) {
                    error.Message = jqXHR.statusText;
                }
            } else if (typeof (errorThrown) === "string") {
                error.Message = errorThrown;
            } else {
                error.Message = errorThrown.message;
            }
            
            if (jqXHR && jqXHR.status) {
                error.Code = '4' + jqXHR.status;
            }

            if (jqXHR.responseText && jqXHR.responseText.charAt(0) == "{") {
                try {
                    var errObj = jQuery.parseJSON(jqXHR.responseText);
                    jQuery.extend(error, errObj || {});
                } catch (ex) { }
            }

            jQuery.extend(error, {
                getMessage: function () { return (this.Message || this.message); },
                getCode: function () { return (this.Code || 0); },
                getTitle: function () { return (this.Title || "Exception"); },
                getDetails: function () { return (this.AdditionalDetails || {}); }
            });

            return error;
        };

        return this.OnError;
    }

});

(function ($) {
    if (!$.ajaxQueue) {
        // jQuery on an empty object, we are going to use this as our Queue
        var ajaxQueue = $({});

        $.ajaxQueue = function (ajaxOpts) {
            var jqXHR,
                dfd = $.Deferred(),
                promise = dfd.promise();

            // run the actual query
            function doRequest(next) {
                jqXHR = $.ajax(ajaxOpts)
                    .done(dfd.resolve)
                    .fail(dfd.reject)
                    .then(next, next);
            }

            // queue our ajax request
            ajaxQueue.queue(doRequest);

            // add the abort method
            promise.abort = function (statusText) {

                // proxy abort to the jqXHR if it is active
                if (jqXHR) {
                    return jqXHR.abort(statusText);
                }

                // if there wasn't already a jqXHR we need to remove from queue
                var queue = ajaxQueue.queue(),
                    index = $.inArray(doRequest, queue);

                if (index > -1) {
                    queue.splice(index, 1);
                }

                // and then reject the deferred
                dfd.rejectWith(ajaxOpts.context || ajaxOpts, [promise, statusText, ""]);
                return promise;
            };

            return promise;
        };
    }
})(jQuery);
